<template>
  <div>
    <div>
      1231231
    </div>
    <div style="position: relative;">
      <canvas id="canvas" style="position: absolute; top: 0; left: 0"></canvas>
    </div>
  </div>
</template>

<script>
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { CSS2DRenderer } from "three/examples/jsm/renderers/CSS2DRenderer";
import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer";
import { RenderPass, EffectComposer, OutlinePass } from "three-outlinepass";
import * as dat from "dat.gui";
import * as THREE from "three";  //引入three.js

let renderer, controls, mixer, compose, moveMixer, labelRenderer, scene, camera, label, gui, timer, setIntervalAlarm, setTimeoutAlarm;
let clock = new THREE.Clock();

const scale = 0.001;
//相机位置
const cameraX = 0;
const cameraY = -8;
const cameraZ = 8;
const canvasWidth = window.innerWidth;
const canvasHeight = window.innerHeight;
const vector = new THREE.Vector3(); //三维坐标对象


export default {
  name: 'index',
  data () {
    return {
      controlsGui: {
        材质: () => {
          if (scene.getObjectByName("turbine")) {
            let Turbine = scene.getObjectByName("turbine");
            if (Turbine.getObjectByName("颜色材质")) {
              let removeTurbine = Turbine.getObjectByName("颜色材质");
              Turbine.remove(removeTurbine);
            } else {
              this.loadTurbine();
            }
          }
        },
        仅部件: false,
        部件损坏告警: () => {
          this.state.showEquipmentAlarm = !this.state.showEquipmentAlarm;
          this.createAlarm();
        },
        风速: 5,
      },
      state: {
        showEquipmentAlarm: false,
        equipmentSelected: {
          name: "",
          position: {},
        },
      }
    }
  },
  mounted () {
    this.initRenderer();
    this.initScence();
    this.initLight();
    this.initMeshes();
    this.initCamera();
    this.initControls();
    this.createTurbineLabel();
    this.animate();
    this.loadGui();
  },

  methods:{
    initRenderer() {
      {
        const canvas = document.getElementById("canvas");
        renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
        renderer.setSize(canvasWidth, canvasHeight);
        renderer.shadowMap.enabled = true;
        // renderer.outputEncoding = THREE.sRGBEncoding; //渲染模型
        document.body.appendChild(renderer.domElement);

        labelRenderer = new CSS2DRenderer();
        labelRenderer.setSize(window.innerWidth, window.innerHeight);
        labelRenderer.domElement.style.position = "absolute";
        // 相对鼠标的相对偏移
        labelRenderer.domElement.style.top = "-16px";
        labelRenderer.domElement.style.left = "0px";
        // //设置.pointerEvents=none，以免模型标签HTML元素遮挡鼠标选择场景模型
        labelRenderer.domElement.style.pointerEvents = "none";
        document.body.appendChild(labelRenderer.domElement);
      };
    },
    initScence() {
      scene = new THREE.Scene();
      scene.background = new THREE.Color(0xe0e0e0);
      scene.toJSON();
      scene.fog = new THREE.Fog(0xe0e0e0, 20, 100); //定义每个物体的雾的类型
    },
    initLight() {
      scene.add(new THREE.AmbientLight(0xffffff, 1));
      const arr = [
        [100, 100, 100],
        [-100, 100, 100],
        [100, -100, 100],
      ];
      arr.forEach((lightArr) => {
        let directionalLight = new THREE.DirectionalLight(0xffffff, 3);
        let [x, y, z] = lightArr;
        directionalLight.position.set(x, y, z);
        scene.add(directionalLight);
      });
    },
    initMeshes() {
// //添加平面
      // const plane = new THREE.Mesh(
      //   new THREE.PlaneGeometry(200, 200),
      //   new THREE.MeshStandardMaterial({
      //     color: 0x999999,
      //     depthWrite: false,
      //   })
      // );
      // plane.position.y = -10;
      // plane.rotation.x = -Math.PI / 2;
      // plane.receiveShadow = true;
      // scene.add(plane);

      //引入gltf模型 风机
      this.loadTurbine();

      //引入gltf模型 电机
      const loader = new GLTFLoader();
      loader.load(
        "3d-gltf-model/equipment.glb",
        (gltf) => {
          let model = gltf.scene;
          scene.add(model);
          model.scale.set(scale, scale, scale);
          model.position.y = -10;
          model.rotation.y = Math.PI / 4;
          model.name = "equipment";
        },
        (xhr) => {
          console.log(
            "加载完成的百分比" + (xhr.loaded / xhr.total) * 100 + "%"
          );
        }
      );
    },
    loadTurbine() {
      const loader = new GLTFLoader();
      if (scene.getObjectByName("turbine")) {
        let removeTurbine = scene.getObjectByName("turbine");
        scene.remove(removeTurbine);
      }
      loader.load(
        "3d-gltf-model/turbine.glb",
        (gltf) => {
          let model = gltf.scene;
          model.name = "turbine";
          model.scale.set(scale, scale, scale);
          model.position.y = -10;
          model.rotation.y = Math.PI / 4;
          scene.add(model);
          //创建动画
          this.createGUI(gltf, gltf.animations);
        },
        (xhr) => {
          console.log(
            "加载完成的百分比" + (xhr.loaded / xhr.total) * 100 + "%"
          );
        }
      );
    },
    initCamera() {
      camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        100000
      );
      camera.position.set(cameraX, cameraY, cameraZ);
    },
    initControls() {
      controls = new OrbitControls(camera, renderer.domElement);
      controls.maxDistance = 20; //设置缩放范围
      controls.maxPolarAngle = Math.PI / 1.2; //设置旋转范围
    },
    createTurbineLabel() {
      let div = document.createElement("div");
      div.style.visibility = "auto";
      div.innerHTML = "info\nxxxxxxxxxxxxxxxxxxxxxx";
      div.style.padding = "4px 10px";
      div.style.color = "#fff";
      div.style.fontSize = "16px";
      div.style.position = "absolute";
      div.style.backgroundColor = "rgba(25,25,25,0.5)";
      div.style.borderRadius = "5px";
      // div元素包装成为css2模型对象CSS2DObject
      label = new CSS2DObject(div);
      div.style.pointerEvents = "none"; //避免HTML标签遮挡三维场景的鼠标事件
      // 设置HTML元素标签在three.js世界坐标中位置
      // label.position.set(0, 0, 0);
      label.visible = false;
      scene.add(label);
    },
    loadGui() {
      gui = new dat.GUI().addFolder("3D风机控件");
      gui.add(this.controlsGui, "材质");
      gui.add(this.controlsGui, "风速", 1, 30, 1).onChange((e) => {
        // 改变风机转速部分，必须使用防抖或者节流
        clearTimeout(timer);
        timer = setTimeout(() => {
          this.loadTurbine();
        }, 1000);
      });
      gui.add(this.controlsGui, "仅部件").onChange((val) => {
        if (val) {
          const canvas = document.getElementById("canvas");
          canvas.addEventListener("click", this.pointerLockControls);
          let removeTurbine = scene.getObjectByName("turbine");
          scene.remove(removeTurbine);
        } else {
          const canvas = document.getElementById("canvas");
          canvas.removeEventListener("click", this.pointerLockControls);
          this.restUnSelected();
          this.loadTurbine();
        }
      });
      gui.add(this.controlsGui, "部件损坏告警");
      gui.open();
    },

    pointerLockControls(event) {
      //获取三维坐标
      vector.set(
        (event.offsetX / canvasWidth) * 2 - 1,
        -(event.offsetY / canvasHeight) * 2 + 1,
        0.5
      );
      let raycaster = new THREE.Raycaster();
      raycaster.setFromCamera(vector, camera);
      let intersects = raycaster.intersectObjects(scene.children);
      if (intersects.length > 0) {
        let selected = intersects[0].object; //取第一个物体
        this.outline([selected]);

        let ismove =
          state.equipmentSelected.name != selected.name ||
          state.equipmentSelected.position.y == selected.position.y;

        this.restUnSelected();

        // 保存所选中的物体
        //一定要用深拷贝
        state.equipmentSelected.name = selected.name;
        state.equipmentSelected.position = JSON.parse(
          JSON.stringify(selected.position)
        );
        // 移动展示物体
        if (ismove) {
          this.updateLabal(intersects[0]);
          this.moveAnimation(
            selected,
            selected.position.x,
            selected.position.y + 1 / scale,
            selected.position.z
          );
        }
      }
    },
    restUnSelected() {
      if (this.state.equipmentSelected.name != "") {
        if (moveMixer) moveMixer.stopAllAction();
        let equipment = scene.getObjectByName(this.state.equipmentSelected.name);
        equipment.position.y = this.state.equipmentSelected.position.y;
        this.outline([]);
        label.visible = false;
      }
    },
    createAlarm() {
// 设备中每个部件随机变红，显示坏掉的警告
      if (this.state.showEquipmentAlarm) {
        const random = parseInt(Math.random() * 9);
        if (scene.getObjectByName("equipment")) {
          let model = scene.getObjectByName("equipment");
          //不同部件材质对象采用不同变量存储
          setIntervalAlarm = setInterval(() => {
            model.traverse((children) => {
              if (children.isMesh) {
                children.material = children.material.clone();
                children.material.emissive.setHex(0);
              }
              if (children.name === equipmentNameList[random]) {
                children.material.emissive.setHex(0xff0000);
                setTimeoutAlarm = setTimeout(() => {
                  children.material.emissive.setHex(
                    0 // 变成红色后设置一个定时器，过了四秒恢复设备原有颜色
                  );
                }, 1000);
              }
            });
          }, 2000);
        }
      } else {
        clearInterval(setIntervalAlarm);
        clearTimeout(setTimeoutAlarm);
        let model = scene.getObjectByName("equipment");
        model.traverse((children) => {
          if (children.isMesh) {
            children.material = children.material.clone();
            children.material.emissive.setHex(0);
          }
        });
      }
    },
    animate() {
      requestAnimationFrame(this.animate);
      renderer.render(scene, camera);
      labelRenderer.render(scene, camera);
      const delta = clock.getDelta();
      if (compose) compose.render();
      if (mixer) mixer.update(delta);
      if (moveMixer) moveMixer.update(delta);
    },
    createGUI(gltf, animations) {
      {
        let model = gltf.scene;
        const animationClip = animations[0];
        animationClip.tracks[0].times = gltf.animations[0].tracks[0].times.map(
          (a) => a / (this.controlsGui.风速 / 5)
        ); // 控制有材质的风机动画速度
        animationClip.tracks[1].times = gltf.animations[0].tracks[1].times.map(
          (a) => a / (this.controlsGui.风速 / 5)
        ); // 控制透明的风机动画速度
        animationClip.duration = 6 / (this.controlsGui.风速 / 5);
        mixer = new THREE.AnimationMixer(model);
        const action = mixer.clipAction(animationClip);
        action.play();
      }
    },
    updateLabal(intersect) {
      label.position.set(
        intersect.point.x - 0.01,
        intersect.point.y + 1,
        intersect.point.z - 0.03
      );
      label.visible = true;
    },
    moveAnimation(mesh, x, y, z) {
      const positionKF = new THREE.VectorKeyframeTrack(
        ".position",
        [0, 1],
        [mesh.position.x, mesh.position.y, mesh.position.z, x, y, z]
      );
      const clip = new THREE.AnimationClip("MoveAction", 1, [positionKF]);
      moveMixer = new THREE.AnimationMixer(mesh);
      const action = moveMixer.clipAction(clip);
      action.setLoop(THREE.LoopOnce);
      action.clampWhenFinished = true; //取消动画重置
      action.play();
    },
    outline(selectedObjects, color = 0x15c5e8) {
      compose = new EffectComposer(renderer);
      let renderPass = new RenderPass(scene, camera);
      let outlinePass = new OutlinePass(
        new THREE.Vector2(window.innerWidth, window.innerHeight),
        scene,
        camera,
        selectedObjects
      );
      outlinePass.renderToScreen = true;
      outlinePass.selectedObjects = selectedObjects;

      compose.addPass(renderPass);
      compose.addPass(outlinePass);
      let params = {
        edgeStrength: 3, //粗
        edgeGlow: 0, //发光
        edgeThickness: 20, //光晕粗
        pulsePeriod: 1, //闪烁
        usePatternTexture: false,
      };

      outlinePass.edgeStrength = params.edgeStrength;
      outlinePass.edgeGlow = params.edgeGlow;
      outlinePass.visibleEdgeColor.set(color);
      outlinePass.hiddenEdgeColor.set(color);

      compose.render(scene, camera);
    }

  }
}
</script>

<style scoped>

</style>
